/*
 * @author btapley
 */
package com.ickbook.ui;

/**
 *
 * @author btapley
 */
public class CSSGrid {

    private int[] cols;
    private int margin = 0;
    private int gutter = 0;
    private String name;

    /**
     * Initialize a new CSS Grid
     */
    public CSSGrid() {
    }

    /**
     * Initialize a new CSS Grid
     * @param name
     */
    public CSSGrid(String name) {
        setName(name);
    }

    /**
     * Initialize a new CSS Grid
     * @param name
     * @param cols
     */
    public CSSGrid(String name, String[] cols) {
        this(name);
        setCols(cols);
    }

    /**
     * Initialize a new CSS Grid
     * @param name
     * @param cols
     * @param gutter
     */
    public CSSGrid(String name, String[] cols, int gutter) {
        this(name, cols);
        setGutter(gutter);
    }

    /**
     * Initialize a new CSS Grid
     * @param name
     * @param cols
     * @param gutter
     * @param margin
     */
    public CSSGrid(String name, String[] cols, int gutter, int margin) {
        this(name, cols, gutter);
        setMargin(margin);
    }

    /**
     * Set the number of pixels on the left and right sides of the CSS Grid
     * @param margin
     */
    public void setMargin(int margin) {
        this.margin = margin;
    }

    /**
     * Get the number of pixels on the left and right sides of the CSS Grid
     * @return
     */
    public int getMargin() {
        return margin;
    }

    /**
     * Set the number of pixels between columns
     * @param gutter
     */
    public void setGutter(int gutter) {
        this.gutter = gutter;
    }

    /**
     * Get the number of pixels between columns
     * @return
     */
    public int getGutter() {
        return gutter;
    }

    /**
     * Set the base class name for the CSS Grid container and it's column names.
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * Get the base class name for the CSS Grid
     * @return
     */
    public String getName() {
        return name;
    }

    /**
     * Add a dot before the name
     * @param name
     * @return
     */
    private String nameForCSS(String name) {
        return "." + name;
    }

    /**
     * Define the column widths array
     * @param cols
     */
    public void setCols(String[] cols) {
        this.cols = new int[cols.length];
        for (int i = 0, l = cols.length; i < l; i++) {
            this.cols[i] = Integer.parseInt(cols[i]);
        }
    }

    /**
     * Get the column name for the specified index. If there is not a column at
     * the specified index, an empty string is returned.
     * @param i
     * @return
     */
    public String getColName(int i) {
        if (name == null) {
            return "";
        }
        return name + "-" + (i + 1);
    }

    /**
     * Get the column name spanning from the start position through the end 
     * position. If there is not a column span for the specified indexes,
     * an empty string is returned.
     * @param start
     * @param end
     * @return
     */
    public String getColName(int start, int end) {
        String _name = getColName(start);
        if (!_name.isEmpty()) {
            Boolean hasEnd = !getColName(end).isEmpty();
            if (hasEnd) {
                _name = _name.concat("-" + (end + 1));
            }
        }
        return _name;
    }

    /**
     * Get the width of the column at the specified index
     * @param i
     * @return
     */
    public int getColWidth(int i) {
        try {
            return cols[i];
        } catch (ArrayIndexOutOfBoundsException e) {
            return 0;
        }
    }

    /**
     * Get the width of the column spanning from the start position through the end position.
     * @param start
     * @param end
     * @return
     */
    public int getColWidth(int start, int end) {
        int width = getColWidth(start);
        if (width > 0) {
            int i = start + 1;
            int stop = Math.min(cols.length, end + 1);
            int span = stop - i;
            double accum = width;
            while (i < stop) {
                width = width + cols[i];
                i++;
            }

            return width + gutter * span;
        }
        return width;
    }

    /**
     * Get the CSS Text suitable for writing to an HTTP response or within a style tag
     * @return
     */
    public String toCSSText() {
        return toCSSText(false);
    }

    /**
     * Get the CSS Text suitable for writing to an HTTP response or within a style tag
     * @param compress
     * @return
     */
    public String toCSSText(Boolean compress) {
        String div = (compress) ? "" : "\n";
        StringBuilder out = new StringBuilder();
        CSSNameGroup breakNames = new CSSNameGroup("clear:left; margin-left:0");
        CSSNameGroup floatNames = new CSSNameGroup("float:left");
        CSSNameGroup gutterNames = new CSSNameGroup("margin-left:" + gutter + "px");

        String curr_name = nameForCSS(name);

        out.append(curr_name + "{_height:1em;*zoom:100%;width:" + getColWidth(0, cols.length - 1) + "px;padding-left:" + margin + "px;padding-right:" + margin + "px}");
        out.append(div);
        out.append(curr_name + ":after{clear:both;content:\"\\0020\";display:block;height:0}");
        out.append(div);

        Integer col = 0;

        while (col < cols.length) {

            curr_name = nameForCSS(getColName(col));

            if (col == 0) {
                breakNames.append(curr_name);
            } else {
                gutterNames.append(curr_name);
            }

            out.append(curr_name + "{width:" + getColWidth(col) + "px}");
            out.append(div);

            floatNames.append(curr_name);

            Integer col_sub = col + 1;

            while (col_sub < cols.length) {

                curr_name = nameForCSS(getColName(col, col_sub));

                if (col == 0) {
                    breakNames.append(curr_name);
                } else {
                    gutterNames.append(curr_name);
                }

                out.append(curr_name + "{width:" + getColWidth(col, col_sub) + "px}");
                out.append(div);

                floatNames.append(curr_name);

                if (col != 0) {

                    Integer col_nest = col;

                    while (col_nest < col_sub) {

                        breakNames.append(curr_name + " " + nameForCSS((col < col_nest) ? getColName(col, col_nest) : getColName(col)));

                        col_nest++;
                    }

                }

                col_sub++;
            }

            col++;
        }

        out.append(breakNames.toString());
        out.append(div);

        out.append(floatNames.toString());
        out.append(div);

        out.append(gutterNames.toString());
        out.append(div);

        return out.toString();
    }

    @Override
    public String toString() {
        return "Grid";
    }

    /**
     * Accumulate CSS Names for a single set of style properties
     */
    private class CSSNameGroup {

        private StringBuilder names;
        private String value;

        public CSSNameGroup() {
            this("{}");
        }

        /**
         * Instantiate a name group with optional style properties value
         * @param value
         */
        public CSSNameGroup(String value) {
            names = new StringBuilder();
            setValue(value);
        }

        /**
         * Add a name to the group
         * @param name
         */
        public void append(String name) {
            if (names.length() > 0) {
                names.append(",");
            }
            names.append(name);
        }

        /**
         * Set the properties that will appear within the curlies ie. {}
         * @param value
         */
        public void setValue(String value) {
            this.value = "{" + value + "}";
        }

        @Override
        /**
         * When names are provided, this returns the names separated with commas
         * and the style properties block. If there are no names, an empty
         * string is returned. 
         */
        public String toString() {
            return (names.length() > 0) ? (names.toString() + this.value) : "";
        }
    }
}
